<?php
/**
 * Created by PhpStorm.
 * User: Luomo
 * Date: 2018/3/8
 * Time: 16:00
 */

namespace Biz\service\trade;

use Biz\model\course\CourseOrderLog as mCourseOrderLog;
use Biz\model\package\PackageOrderLog;
use Biz\model\trade\Order as mOrder;
use Biz\model\trade\OrderPay as mOrderPay;
use Biz\module\Packages\Pay\Alipay;
use Biz\module\Packages\Pay\AlipayQrcode;
use Biz\module\Packages\Pay\BalanceInterface;
use Biz\module\Packages\Pay\CustomizePayWay;
use Biz\module\Packages\Pay\IAP;
use Biz\module\Packages\Pay\MiniProgram;
use Biz\module\Packages\Pay\PayAbstruct;
use Biz\module\Packages\Pay\TheParagraph;
use Biz\module\Packages\Pay\Tiles;
use Biz\module\Packages\Pay\TuanMiniProgram;
use Biz\module\Packages\Pay\Wechat;
use Biz\module\Packages\Pay\WechatBiz;
use Biz\module\Packages\Pay\WechatBizQrcode;
use Biz\module\Packages\Pay\WechatQrcode;
use Biz\module\Sys\ErrorConst;
use Biz\pockets\constInterface\PayConfig;
use Biz\pockets\serviceTrait\_Async;
use Biz\service\bizAd\AdRechargeOrder;
use Biz\service\brand\Order as BrandOrder;
use Biz\service\course\CourseOrder;
use Biz\service\package\PackageOrder;
use Biz\tools\OrderNo;
use Doraemon\amqp\Queue;
use Doraemon\pockets\publicTrait\CheckConstants;
use Doraemon\service\Designer\Order as DesignerOrder;
use Doraemon\tools\Log;

class Pay implements PayConfig
{
    use CheckConstants;
    use _Async;

    const _CONFIG_FILE_ = 'pay';


    public static function getPayOrderInfo($orderInfo, $payWay, $orderType = self::_PAY_ORDER_TYPE_BIZ)
    {
        if (self::checkConstants(self::_CONST_PAY_WAYS_PREFIX, $payWay)) {
            $payGateWay = self::getPayGateWay($payWay);
            return $payGateWay::createOrder([
                'order_no' => $orderInfo['order_no'],
                'pay_ver' => isset($orderInfo['pay_ver']) ? $orderInfo['pay_ver'] : 0,
                'pay_amount' => $orderInfo['pay_amount'],
                'paystop_time' => isset($orderInfo['paystop_time']) ? $orderInfo['paystop_time'] : NULL,
                'title' => isset($orderInfo['title']) ? $orderInfo['title'] : ""
            ], [
                'open_id' => isset($orderInfo['open_id']) ? $orderInfo['open_id'] : NULL,
                'order_type' => $orderType
            ]);
        } else {
            throw new \Exception('支付方式错误');
        }
    }

    protected static function _closeOrder($orderNo)
    {
        $payInfos = mOrderPay::getPayInfoByOrderNo($orderNo);
        if ($payInfos) {
            foreach ($payInfos as $payInfo) {
                if ($payInfo['status'] == mOrderPay::STATUS_NOTPAY) {
                    $payGateWay = self::getPayGateWay($payInfo['pay_way']);
                    $payGateWay::closeOrder($orderNo, $payInfo['pay_ver']);
                }
            }
        }
    }

    public static function closeOrder($orderNo)
    {
        $isPaid = self::checkOrderPayStatus($orderNo);
        if ($isPaid) {
            throw new \Exception('该订单已支付，无法关闭');
        }
        $isParentNo = OrderNo::isParentNo($orderNo);
        if ($isParentNo) {
            return self::_closeOrder($orderNo);
        } else {
            self::_closeOrder($orderNo);
            $parentNo = mOrder::getOrderParentNo($orderNo);
            return self::closeOrder($parentNo);
        }
    }

    protected static function getPayGateWay($payWay)
    {
        if (self::checkConstants(self::_CONST_PAY_WAYS_PREFIX, $payWay)) {
            switch ($payWay) {
                case static::_PAY_WAYS_ALIPAY:
                    return Alipay::getInstance();
                    break;
                case static::_PAY_WAYS_WECHAT:
                    return Wechat::getInstance();
                    break;
                case static::_PAY_WAYS_MINIPROGRAM:
                    return MiniProgram::getInstance();
                    break;
                case static::_PAY_WAYS_TUAN_MINIPROGRAM:
                    return TuanMiniProgram::getInstance();
                    break;
                case static::_PAY_WAYS_THE_PARAGRAPH_MINIPROGRAM:
                    return TheParagraph::getInstance();
                    break;
                case static::_PAY_WAYS_ALIPAY_QRCODE:
                    return AlipayQrcode::getInstance();
                    break;
                case static::_PAY_WAYS_WECHAT_QRCODE:
                    return WechatQrcode::getInstance();
                    break;
                case static::_PAY_WAYS_CUSTOMIZE_PAY_WAY:
                    return CustomizePayWay::getInstance();
                    break;
                case static::_PAY_WAYS_IAP:
                    return IAP::getInstance();
                    break;
                case static::_PAY_WAYS_TILES:
                    return Tiles::getInstance();
                    break;
                case static::_PAY_WAYS_WECHAT_BIZ:
                    return WechatBiz::getInstance();
                    break;
                case static::_PAY_WAYS_WECHAT_BIZ_QRCODE:
                    return WechatBizQrcode::getInstance();
                    break;
            }
        } else {
            throw new \Exception('支付方式错误');
        }
    }

    public static function refundAmount($orderNo, $refundOrderNo, $payWay, $payNo, $refundAmount)
    {
        if (self::checkConstants(self::_CONST_PAY_WAYS_PREFIX, $payWay)) {
            Log::pay_refund_info($payWay . "#" . $orderNo, [
                $orderNo,
                $refundOrderNo,
                $payWay,
                $payNo,
                $refundAmount
            ]);
            if ($payWay != self::_PAY_WAYS_CUSTOMIZE_PAY_WAY) {
                $payInfo = mOrderPay::getPayInfo($payNo, $payWay);
            } else {
                $payInfo = mOrderPay::getPayInfoByOrderNoAndPayWay($orderNo, self::_PAY_WAYS_CUSTOMIZE_PAY_WAY);
            }
            if ($payInfo && $payInfo['pay_amount'] >= $refundAmount) {
                $payGateWay = self::getPayGateWay($payWay);
                return $payGateWay::refundOrder($orderNo, $refundOrderNo, $payNo, $refundAmount, $payInfo);
            } else {
                Log::pay_refund_error("支付信息异常：" . $orderNo,
                    [
                        $orderNo,
                        $refundOrderNo,
                        $payWay,
                        $payNo,
                        $refundAmount,
                        $payInfo
                    ]);
                throw new \Exception('支付信息异常');
            }
        } else {
            throw new \Exception('支付方式错误');
        }
    }

    public static function payCallBack($callBackData, $payWay)
    {
        Log::pay_3rdCallback_info($payWay, $callBackData);
        if (self::checkConstants(self::_CONST_PAY_WAYS_PREFIX, $payWay)) {
            $payGateWay = self::getPayGateWay($payWay);
            $payInfo = $payGateWay::payCallBack($callBackData);
            if ($payInfo) {
                self::afterPaySuccess($payInfo, $payWay, $payInfo['order_type']);
            }
        } else {
            throw new \Exception('支付方式错误');
        }
    }

    protected static function afterPaySuccess($payInfo, $payWay, $orderType = self::_PAY_ORDER_TYPE_BIZ)
    {
        $orderType = $payInfo['order_type'] ?? $orderType;
        switch ($orderType) {
            case self::_PAY_ORDER_TYPE_BIZ:
                self::afterBizOrderPaySuccess($payInfo, $payWay);
                break;
            case self::_PAY_ORDER_TYPE_DESIGNER:
                self::afterDesignerOrderPaySuccess($payInfo, $payWay);
                break;
            case self::_PAY_ORDER_TYPE_COURSE:
                self::afterCourseOrderPaySuccess($payInfo, $payWay);
                break;
            case self::_PAY_ORDER_TYPE_PACKAGE:
                self::afterPackageOrderPaySuccess($payInfo, $payWay);
                break;
            case self::_PAY_ORDER_TYPE_BRAND:
                self::afterBrandOrderPaySuccess($payInfo, $payWay);
                break;
            case self::_PAY_ORDER_TYPE_AD:
                self::afterADOrderPaySuccess($payInfo, $payWay);
                break;
        }
    }

    private static function afterBizOrderPaySuccess($payInfo, $payWay)
    {
        Log::pay_orderCallback_info($payWay, $payInfo);
        try {
            Order::afterPaySuccessCallback($payInfo['order_no'], $payWay,
                $payInfo['pay_no'], $payInfo['pay_amount'], $payInfo['pay_time'], $payInfo['pay_ver']);
        } catch (\Exception $e) {
            Log::pay_orderCallback_error($payWay . "#订单状态异常:" . $payInfo['order_no'] . "#" . $e->getCode(), $e);
            $errorCode = $e->getCode();
            if ($errorCode == ErrorConst::ERROR_ORDER_IS_CANCEL || $errorCode == ErrorConst::ERROR_ORDER_PAY_AGAIN_ERROR) {
                self::_async($payInfo, $payWay, $errorCode);
            }
        }
    }


    private static function afterDesignerOrderPaySuccess($payInfo, $payWay)
    {
        try {
            DesignerOrder::callbackPaySucc($payInfo['order_no'], $payInfo['pay_no'], $payWay,
                $payInfo['pay_amount'], $payInfo['pay_time']);
        } catch (\Exception $e) {
            Log::pay_orderCallback_error($payWay . "#设计师认证订单状态异常:" . $payInfo['order_no'] . "#" . $e->getCode(), $e);
        }
    }

    private static function afterCourseOrderPaySuccess($payInfo, $payWay)
    {
        try {
            CourseOrder::callbackPaySucc($payInfo['order_no'], $payWay, $payInfo['pay_no'], $payInfo['pay_amount'], $payInfo['pay_time']);
        } catch (\Exception $e) {
            CourseOrder::addOrderLog($payInfo['order_no'], 0, mCourseOrderLog::LOG_TYPE_PAY_ERROR, [
                'pay_way' => $payWay,
                'pay_amount' => $payInfo['pay_amount'],
                'pay_time' => $payInfo['pay_time']
            ]);
            Log::pay_orderCallback_error($payWay . "#付费课订单状态异常:" . $payInfo['order_no'] . "#" . $e->getCode(), $e);
        }
    }

    private static function afterPackageOrderPaySuccess($payInfo, $payWay)
    {
        try {
            PackageOrder::callbackPaySucc($payInfo['order_no'], $payWay, $payInfo['pay_no'], $payInfo['pay_amount'], $payInfo['pay_time']);
        } catch (\Exception $e) {
            $data = [
                'pay_way' => $payWay,
                'pay_amount' => $payInfo['pay_amount'],
                'pay_time' => $payInfo['pay_time']
            ];
            $log = [
                'log_data' => json_encode($data, JSON_UNESCAPED_UNICODE),
                'log_type' => PackageOrderLog::LOG_TYPE_4,
                'log' => '订单修改失败',
                'order_no' => $payInfo['order_no'],
            ];
            PackageOrderLog::add($log);
            Log::info("大礼包订单状态异常:", [$log, $e->getMessage(), $e->getCode(), $e->getLine()]);
        }
    }

    private static function afterBrandOrderPaySuccess($payInfo, $payWay)
    {
        try {
            BrandOrder::callbackPaySuccess($payInfo['order_no'], $payInfo['pay_no'], $payWay, $payInfo['pay_amount'], $payInfo['pay_time']);
        } catch (\Exception $e) {
            Log::pay_orderCallback_error($payWay . "#品牌认证订单状态异常:" . $payInfo['order_no'] . "#" . $e->getCode(), $e);
        }
    }

    private static function afterADOrderPaySuccess($payInfo, $payWay)
    {
        try {
            AdRechargeOrder::callbackPaySuccess($payInfo['order_no'], $payInfo['pay_no'], $payWay, $payInfo['pay_amount'], $payInfo['pay_time']);
        } catch (\Exception $e) {
            Log::pay_orderCallback_error($payWay . "#AD充值订单状态异常:" . $payInfo['order_no'] . "#" . $e->getCode(), $e);
        }
    }

    public static function _async_afterBizOrderPaySuccess($payInfo, $payWay, $errorCode)
    {
        if ($errorCode == ErrorConst::ERROR_ORDER_IS_CANCEL || $errorCode == ErrorConst::ERROR_ORDER_PAY_AGAIN_ERROR) {
            Queue::insertQueue("queue/WechatNotice/toTag", [
                "totag" => \Biz\pockets\constInterface\BizWechatTagConfig::TAG_BIZ_RD_SYSTEM,
                "content" => $payWay . '支付回调错误：' . $errorCode . json_encode($payInfo)
            ], Queue::QUEUE_BIZ);
            return true;
            $payGateWay = self::getPayGateWay($payWay);
            $payGateWay::cancelOrder($payInfo['order_no'], $payInfo);
        }
    }

    public static function checkOrderPayStatus($orderNo, $orderType = self::_PAY_ORDER_TYPE_BIZ)
    {
        switch ($orderType) {
            case self::_PAY_ORDER_TYPE_BIZ:
                return self::checkBizOrderPayStatus($orderNo);
                break;
            case self::_PAY_ORDER_TYPE_DESIGNER:
            case self::_PAY_ORDER_TYPE_COURSE:
            case self::_PAY_ORDER_TYPE_PACKAGE:
            case self::_PAY_ORDER_TYPE_BRAND:
            case self::_PAY_ORDER_TYPE_AD:
                return self::_checkOrderPayStatus($orderNo, $orderType);
                break;
        }
    }


    private static function checkBizOrderPayStatus($orderNo)
    {
        $isParentNo = OrderNo::isParentNo($orderNo);
        if ($isParentNo) {
            return self::_checkOrderPayStatus($orderNo);
        } else {
            $isPaid = self::_checkOrderPayStatus($orderNo);
            if ($isPaid) {
                return $isPaid;
            } else {
                $parentNo = mOrder::getOrderParentNo($orderNo);
                return self::checkOrderPayStatus($parentNo);
            }
        }
    }

    private static function _checkOrderPayStatus($orderNo, $orderType = self::_PAY_ORDER_TYPE_BIZ)
    {
        $isPaid = false;
        $payInfos = mOrderPay::getPayInfoByOrderNo($orderNo, $orderType);
        if ($payInfos) {
            foreach ($payInfos as $payInfo) {
                $payGateWay = self::getPayGateWay($payInfo['pay_way']);
                switch ($payInfo['status']) {
                    case mOrderPay::STATUS_NOTPAY:
                        $paidInfo = $payGateWay::queryOrder($payInfo['order_no'], $payInfo['pay_ver']);
                        if ($paidInfo) {
                            $isPaid = true;
                            self::afterPaySuccess($paidInfo, $payInfo['pay_way'], $orderType);
                        }
                        break;
                    case mOrderPay::STATUS_PAIED:
                        $isPaid = true;
                        break;
                    case mOrderPay::STATUS_CANCEL:
                    case mOrderPay::STATUS_CLOSED:
                        break;
                }
            }
        }
        return $isPaid;
    }

    public static function getPayWays($payWays = PayAbstruct::_APP_PAY_WAYS_LIST, $uid = NULL)
    {
        $payWays = PayAbstruct::getPayWays($payWays);
        foreach ($payWays as &$payWay) {
            $payGateWay = self::getPayGateWay($payWay['pay_way']);
            if (new $payGateWay instanceof BalanceInterface) {
                $payWay['balance'] = $payGateWay::getBalance($uid);
            }
        }
        return $payWays;
    }
}